<?php

/*
	Class: Cts_Db_Table_Abstract

	About: Author
		Jaybill McCarthy

	About: License
		<http://communit.as/docs/license>

	About: See Also
		- Zend_Db_Table
*/
abstract class Cts_Db_Table_Abstract extends Zend_Db_Table {

	/* Group: Properties */

	/*
		Property: $_errors
	*/
	public $_errors;

	/*
		Property: $_keyword_search_field_names
	*/
	public $_keyword_search_field_names = array();

	/*
		Property: $_module_id
	*/
	public $_module_id = "default";

	private $_memCacheOn = false;
	private $_memcache = null;

	/* Group: Constructors */

	/*
		Constructor: Cts_Db_Table_Abstract
			Calls the parent's constructor and instantiates a logger. Also sets up a DB adapter and the _cts_plugin variable.

		Arguments:
			config (optional) - TBD
	*/
	function Cts_Db_Table_Abstract($config = null) {		
		$this->_errors = array();
		if (isset($this->_use_adapter)) {
			$dbAdapters = Zend_Registry::get('dbAdapters');
			$config = ($dbAdapters[$this->_use_adapter]);
		}		
		$this->_cts_plugin = Cts_Plugin::getInstance();
		return parent::__construct($config);
	}

	/* Group: Instance Methods */

	/*
		Function: getCountByWhereClause
			Gets a count of records using the supplied where clause.

		Arguments:
			whereclause (optional) - A string with your where clause in it. The word "where" is not needed. If you provide no where clause, a count of all rows will be returned.

		Returns:
			An integer representing the total number of records for that where clause.
	*/
	public function getCountByWhereClause($whereclause = null) {
		$db = $this->getAdapter();
		$sql = "SELECT count(*) from " . $this->_name;
		if (!is_null($whereclause)) {
			$sql .= " where " . $whereclause;
		}
		$total = $db->fetchOne($sql);
		return $total;
	}

	/*
		Function: update
			Updates the table with the supplied data array.
			You must supply a where clause if you want to limit what gets updated,
			otherwise all rows will get the update.

		Arguments:
			data - An array with a key-value pair for each column in the table.
			where - A where clause string to limit the rows that get updated. If the where clause is empty, all rows will be updated with the data argument.

		Returns:
			TBD
	*/
	public function update(array $data, $where) {
		
		$metadata = $this->info();
		$columns = $metadata['cols'];
		$timestamp = date("Y-m-d H:i:s") ;
		if (in_array('updated_on', $columns)) {
			if (!in_array('updated_on', $data)) {
				$data['updated_on'] = $timestamp;
			}
		}
		
		$params = array(
			"data" => $data,
			"where" => $where,
			"errors" => $this->_errors,
			"table" => $this->_name,
			"module" => $this->_module_id,
			"primary" => $this->_primary,			
		);
		
		if(isset($this->_use_adapter)){
			$params['use_adapter'] = $this->_use_adapter;
		} else {
			$params['use_adapter'] = null;
		}
		
		// rethrowing exceptions here because of a weird php issue where the trace isn't getting passed
		try{
			$params = Cts_Plugin::getInstance()->doFilter('db_table_update', $params);		
		} catch (Exception $e){
			throw($e);	
		}		
		
		if(count($params['errors']) == 0){
			return parent::update($params['data'], $params['where']);
		} else {
			$this->_errors = $params['errors'];
			return false;
		}
	}

	/*
		Function: insert
	*/
	public function insert(array $data) {
		$params = array(
			"data" => $data,			
			"errors" => $this->_errors,
			"table" => $this->_name,
			"module" => $this->_module_id,
			"primary" => $this->_primary,			
		);
		
		if(isset($this->_use_adapter)){
			$params['use_adapter'] = $this->_use_adapter;
		} else {
			$params['use_adapter'] = null;
		}
		
		// rethrowing exceptions here because of a weird php issue where the trace isn't getting passed
		try{
			$params = Cts_Plugin::getInstance()->doFilter('db_table_insert', $params);		
		} catch (Exception $e){
			throw($e);	
		}		
		
		if(count($params['errors']) == 0){			
			$params['insert_id'] = parent::insert($params['data']);			 
			Cts_Plugin::getInstance()->doAction('db_table_post_insert',$params);
			return $params['insert_id'];
		} else {
			$this->_errors = $params['errors'];
			return false;
		}				
		
	}

	/*
		Function: dumpData
			Convert a rowset into an XML document.
	*/
	public function dumpData() {
		$data = $this->fetchAll();
		$xml = Cts_Api::makeXml($data->toArray(), $this->_name);
		if (count($data) > 0) {
			dd($data);			
		}
		return $xml;
	}

	/*
		Function: fetchRowArray

		Arguments:
			where - A where clause to search by.

		Returns: array or null
	*/
	function fetchRowArray($where) {
		$tmp_row = $this->fetchRow($where);
		if (!is_null($tmp_row)) {
			$tmp_row = $tmp_row->toArray();
			return $tmp_row;
		} else {
			return null;
		}
	}

	/*
		Function: findArray
			Fetches one row from the database using an ID.
			Pass an array for composite primary keys.
	
		Arguments:
			id - The ID of the row to find.
	
		Returns: one row as an array or null

		About: See Also
			- Zend_Db_Table::find()
	*/
	function findArray() {
		$command = "\$tmp_row = \$this->find(";
		$args = func_get_args();
		for ($i = 0; $i < count($args); $i++) {
			$command .= "\"".$args[$i]."\"";
			if ($i + 1 < count($args)) {
				$command .= ", ";
			}
		}
		$command .= ");";
		eval($command);
		if (!is_null($tmp_row) && count($tmp_row) >= 1) {
			$tmp_row = $tmp_row->toArray();
			return $tmp_row[0];
		} else {
			return null;
		}
	}

	/*
		Function: fetchAllArray
			Performs a fetchAll but always returns an array.

		Arguments: TBD

		Returns: multidimensional array of data, or an empty array
	*/
	function fetchAllArray($where = null, $order = null, $limit = null, $page = null) {
		$tmp_rows = $this->fetchAll($where, $order, $limit, $page);
		if (!is_null($tmp_rows)) {
			return $tmp_rows->toArray();
		} else {
			return array();
		}
	}

	/*
		Function: fetchAllArrayByKeywords
			Search a table with multiple keywords.

		Arguments:
			keywords - A string containing a space-delimited list of words to search for.

		Returns: array of records or an empty array
	*/
	function fetchAllArrayByKeywords($keywords) {
		$where = $this->getWhereClauseForKeywords($keywords, $fieldnames);
		return $this->fetchAllArray($where);
	}

	/*
		Function: getWhereClauseForKeywords
			A model-agnostic way to get a where clause to search a table with multiple keywords.

		Arguments:
			keywords - A string containing a space-delimited list of words to search for.

		Returns: string
	*/
	function getWhereClauseForKeywords($keywords) {
		$keywords = split(" ", $keywords);
		// TODO - this is the old and [not] busted way of doing it - convert this to the new hotness Zend_Db_Statement way of doing things
		$where = "1 = 1";
		foreach ($keywords as $word) {
			$where .= " and (0 = 1";
			foreach ($this->_keyword_search_field_names as $field) {
				$where .= $this->getAdapter()->quoteInto(' or '.$field.' like ?', "%".$word."%");
			}
			$where .= ")";
		}
		return $where;
	}

	/*
		Function: deleteById
			Deletes one row using an ID, assuming the table's ID is a single column named "id".

		Arguments:
			id - The ID of the row to delete.

		Returns: void
	*/
	function deleteById($id) {
		$where = $this->getAdapter()->quoteInto("id = ?", $id);
		$this->delete($where);
	}

	/* Group: Private or Protected Methods */

	/*
		Function: _T
			Translate a string using the module's language files.
	*/
	protected function _T($key, $replace = null) {
		return Cts_Translate($this->_module_id, $key, $replace);
	}
	
	public function fetchAll($where = null,$order = null,$count = null,$offset = null){	
		
		if($this->_memCacheOn){
			
			$args = func_get_args();
			$cache_signature = base64_encode(implode( "|||" , $args ));
			$alnum = new Zend_Filter_Alnum();
			$cache_signature = trim($alnum->filter($cache_signature));
			Cts_Log::info("using memcache for ".$cache_signature);
			
			// try to load from cache
			if(!$result = $this->_memcache->load($cache_signature)) {	
				$result = parent::fetchAll($where,$order,$count,$offset);
				Cts_Log::info("saved cache to ".$cache_signature);
				$this->_memcache->save($result,$cache_signature);
			}else{
				Cts_Log::info("loaded cache from ".$cache_signature);
			}
		} else {	
			$result = parent::fetchAll($where,$order,$count,$offset);
		}
				
		return $result;		
	}
	
	public function memCacheOn(){
		if(Zend_Registry::isRegistered("memcache")){
			Cts_Log::info("using memcache for ".$this->_name);
			$this->_memcache = Zend_Registry::get("memcache");
			$this->_memCacheOn = true;
			
			return true;
		} else {
			return false;
		}
	}

	public function memCacheOff(){
		$this->_memCacheOn = false;
	}

}
